package demo;

import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.util.Random;

public class Bouncer extends AnimationComponent {
	public static void main(String[] args) {
		final Bouncer bouncer = new Bouncer();
		Frame f = new AnimationFrame(bouncer);
		f.setFont(new Font("Serif", Font.PLAIN, 12));
		Panel controls = new Panel();
		controls.add(bouncer.createCheckbox("Anti.", Bouncer.ANTIALIASING));
		controls.add(bouncer.createCheckbox("Trans.", Bouncer.TRANSFORM));
		controls.add(bouncer.createCheckbox("Gradient", Bouncer.GRADIENT));
		controls.add(bouncer.createCheckbox("Outline", Bouncer.OUTLINE));
		controls.add(bouncer.createCheckbox("Dotted", Bouncer.DOTTED));
		controls.add(bouncer.createCheckbox("Axes", Bouncer.AXES));
		controls.add(bouncer.createCheckbox("Clip", Bouncer.CLIP));
		f.add(controls, BorderLayout.NORTH);
		f.setVisible(true);
	}

	// Tweakable variables
	private boolean mAntialiasing, mGradient, mOutline;
	private boolean mTransform, mDotted, mAxes, mClip;
	// ...and the constants that represent them. See setSwitch().
	public static final int ANTIALIASING = 0;
	public static final int GRADIENT = 1;
	public static final int OUTLINE = 2;
	public static final int TRANSFORM = 3;
	public static final int DOTTED = 4;
	public static final int AXES = 5;
	public static final int CLIP = 6;
	private float[] mPoints;
	private float[] mDeltas;
	private float mTheta;
	private int mN;
	private Shape mClipShape;

	public Bouncer() {
		mN = 38;
		mPoints = new float[mN];
		mDeltas = new float[mN];
		Random random = new Random();
		for (int i = 0; i < mN; i++) {
			mPoints[i] = random.nextFloat() * 500;
			mDeltas[i] = random.nextFloat() * 3;
		}
		// Make sure points are within range.
		addComponentListener(new ComponentAdapter() {
			public void componentResized(ComponentEvent ce) {
				Dimension d = getSize();
				for (int i = 0; i < mN; i++) {
					int limit = ((i % 2) == 0) ? d.width : d.height;
					if (mPoints[i] < 0)
						mPoints[i] = 0;
					else if (mPoints[i] >= limit)
						mPoints[i] = limit - 1;
				}
			}
		});
	}

	public void setSwitch(int item, boolean value) {
		switch (item) {
		case ANTIALIASING:
			mAntialiasing = value;
			break;
		case GRADIENT:
			mGradient = value;
			break;
		case OUTLINE:
			mOutline = value;
			break;
		case TRANSFORM:
			mTransform = value;
			break;
		case DOTTED:
			mDotted = value;
			break;
		case AXES:
			mAxes = value;
			break;
		case CLIP:
			mClip = value;
			break;
		default:
			break;
		}
	}

	protected Checkbox createCheckbox(String label, final int item) {
		Checkbox check = new Checkbox(label);
		check.addItemListener(new ItemListener() {
			public void itemStateChanged(ItemEvent ie) {
				setSwitch(item, (ie.getStateChange() == ie.SELECTED));
			}
		});
		return check;
	}

	public void timeStep() {
		Dimension d = getSize();
		for (int i = 0; i < mN; i++) {
			float value = mPoints[i] + mDeltas[i];
			int limit = ((i % 2) == 0) ? d.width : d.height;
			if (value < 0 || value > limit) {
				mDeltas[i] = -mDeltas[i];
				mPoints[i] += mDeltas[i];
			} else
				mPoints[i] = value;
		}
		mTheta += Math.PI / 192;
		if (mTheta > (2 * Math.PI))
			mTheta -= (2 * Math.PI);
	}

	public void paint(Graphics g) {
		Graphics2D g2 = (Graphics2D) g;
		setAntialiasing(g2);
		setClip(g2);
		setTransform(g2);
		Shape shape = createShape();
		setPaint(g2);
		// Fill the shape.
		g2.fill(shape);
		// Maybe draw the outline.
		if (mOutline) {
			setStroke(g2);
			g2.setPaint(Color.blue);
			g2.draw(shape);
		}
		drawAxes(g2);
	}

	protected void setAntialiasing(Graphics2D g2) {
		if (mAntialiasing == false)
			return;
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
	}

	protected void setClip(Graphics2D g2) {
		if (mClip == false)
			return;
		if (mClipShape == null) {
			Dimension d = getSize();
			FontRenderContext frc = g2.getFontRenderContext();
			Font font = new Font("Serif", Font.PLAIN, 144);
			String s = "Spoon!";
			GlyphVector gv = font.createGlyphVector(frc, s);
			Rectangle2D bounds = font.getStringBounds(s, frc);
			mClipShape = gv.getOutline(
					(d.width - (float) bounds.getWidth()) / 2,
					(d.height + (float) bounds.getHeight()) / 2);
		}
		g2.clip(mClipShape);
	}

	protected void setTransform(Graphics2D g2) {
		if (mTransform == false)
			return;
		Dimension d = getSize();
		g2.rotate(mTheta, d.width / 2, d.height / 2);
	}

	protected Shape createShape() {
		GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD,
				mPoints.length);
		path.moveTo(mPoints[0], mPoints[1]);
		for (int i = 2; i < mN; i += 6)
			path.curveTo(mPoints[i], mPoints[i + 1], mPoints[i + 2],
					mPoints[i + 3], mPoints[i + 4], mPoints[i + 5]);
		path.closePath();
		return path;
	}

	protected void setPaint(Graphics2D g2) {
		if (mGradient) {
			GradientPaint gp = new GradientPaint(0, 0, Color.yellow, 50, 25,
					Color.red, true);
			g2.setPaint(gp);
		} else
			g2.setPaint(Color.orange);
	}

	protected void setStroke(Graphics2D g2) {
		if (mDotted == false)
			return;
		// Create a dotted stroke.
		Stroke stroke = new BasicStroke(1, BasicStroke.CAP_BUTT,
				BasicStroke.JOIN_ROUND, 10, new float[] { 4, 4 }, 0);
		g2.setStroke(stroke);
	}

	protected void drawAxes(Graphics2D g2) {
		if (mAxes == false)
			return;
		g2.setPaint(getForeground());
		g2.setStroke(new BasicStroke());
		Dimension d = getSize();
		int side = 20;
		int arrow = 4;
		int w = d.width / 2, h = d.height / 2;
		g2.drawLine(w - side, h, w + side, h);
		g2.drawLine(w + side - arrow, h - arrow, w + side, h);
		g2.drawLine(w, h - side, w, h + side);
		g2.drawLine(w + arrow, h + side - arrow, w, h + side);
	}
}
